﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Threading;


namespace MKIB3Game
{
    public class Parser : IParser
    {
        int serverVer;
        String line = null;
        String[] part = new String[3];
        static String connypfad = "E://Users/Conny/Dropbox/Texte_(Laptop-Sync-25)/Studium MKINF/mkib3_Inf3/praktikum/p4/output.txt";
        static String elipfad = "c://output.txt";
        StreamReader sr;
        String[] s = null;
        IBackend backend;
        IConnector connector;
        int TIME_OUT_CONNECTOR_NULL = 1000;
        int TIME_OUT_WAITING_FOR_NEW_MESSAGE = 100;
        bool MAINLOOP = true;
        int MAPCELL_ERROR = 0;
        int MAPCELL_PROP_ERROR = 0;

        public Parser()
        {
           //---------- begin:Testmethode
           // sr = new StreamReader(connypfad); 
           // this.start();
           //---------- end:Testmethode
        }

        public Parser(IBackend b) : this()
        {
            this.backend = b;
            Console.WriteLine("parser mit backend verknüpft");
            Thread t = new Thread(this.start);
            t.Name = "parser thread";
            t.Start();
            Console.WriteLine("create Connector from parser");
            connector = new Connector((IParser)this);   
        }

        public void parseToString(Object o)
        {
            string s = "";
            Console.WriteLine("parse to string called");
            //Diese methode soll opjekte vom backend parsen und an den sender weitergeben
            // das in einen string umgewandelte object kann dann mit connector.send(message) an den server gesendet werden

            // wenn string, dann serveranfrage
            if (o is string || o is String)
            {
                connector.send((string)o);
            } 

            if (o is MovementEnum)
            {
                MovementEnum mov = (MovementEnum)o;
                s = "ask:mv:";
                switch (mov)
                { 
                    case MovementEnum.UP: s+="up";break;
                    case MovementEnum.DOWN: s += "dwn"; break;
                    case MovementEnum.LEFT: s += "lft"; break;
                    case MovementEnum.RIGHT: s += "rgt"; break;
                }
            }


            if (o is Message)
                {
                    Message m=(Message)o;
                    s = "ask:say:" + m.getTxt();
                }

            if (o is ServerEnum)
                {
                    ServerEnum serv = (ServerEnum)o;
                    s = "get:";
                    switch (serv)
                    {
                        case ServerEnum.MAP: s += "map"; break;
                        case ServerEnum.ONLINE: s += "online"; break;
                        case ServerEnum.ENTS: s += "ents"; break;
                        case ServerEnum.PLAYER: s += "player"; break;
                        case ServerEnum.ME: s += "me"; break;
                        case ServerEnum.TIME: s += "time"; break;
                        case ServerEnum.BYE: s = "ask:bye"; break;
                    }
                }

              if (o is ActionEnum){
                    ActionEnum ac = (ActionEnum)o;
                    s = "ask:set:";
                    switch (ac)
                    {

                        case ActionEnum.ALCHEMY: s += "skirm:alchemy"; break;
                        case ActionEnum.BUNNY: s += "shunt:bunny"; break;
                        case ActionEnum.FIGHT: s += "dragon:fight"; break;
                        case ActionEnum.MAGIC: s += "skirm:magic"; break;
                        case ActionEnum.REST: s += "dragon:rest"; break;
                        case ActionEnum.STAG: s += "shunt:stag"; break;
                        case ActionEnum.SWORD: s += "skirm:alchemy"; break;
                    }}

              if (o is int|| o is Int32){
                    int i=(int)o;
                    s="get:ent:"+i;
                }
             
               if( o is Object[]){
                    s="ask:chal:";
                    Object[] ar=(Object[])o;
                    if(ar[0] is ChallengeTypeEnum && ar[1] is int){
                    ChallengeTypeEnum challange=(ChallengeTypeEnum) ar[0];
                    int player=(int)ar[1];

                        switch(challange){
                            case ChallengeTypeEnum.DRAGON: s+="dragon:"+player;break;
                            case ChallengeTypeEnum.SKIRMISH: s+="skirm:"+player;break;
                            case ChallengeTypeEnum.STAGHUNT: s+="shunt:"+player;break;
                        }
                    }

                    if (ar[0] is ServerEnum && ar[1] is string)
                        {
                            String pos1= (string) ar[1];
                            ServerEnum pos0 = (ServerEnum)ar[0];
                           if(pos0.Equals(ServerEnum.RENAME)){ s="ask:rn:"+pos1;
                           }
                }
                }
                connector.send(s);
            }        // end parseToString

        public void start()
        {
            //mainloop receiver
            while (MAINLOOP)
            {
                if (connector != null)
                {
                    nextLine();
                    if (s != null) // sollte hier IMMER != null sein, da nextLine() nie null zurück gibt
                    {
                        Console.WriteLine("start()----------[" + s[0] + ":" + s[1] + "]");
                        if (s[0].Equals("begin"))
                        {
                            if (s[1].Equals("server")) { nextLine(); serverVer = Convert.ToInt32(s[1]); nextLine(); }
                            else { servermess(); }
                        }
                    }
                }
                else
                { // timeout if connector does not exist
                    Console.WriteLine("Connector is Null - sleep for " + TIME_OUT_CONNECTOR_NULL + "ms");
                    Thread.Sleep(TIME_OUT_CONNECTOR_NULL);
                    // start();
                }
            }// ende mainloop
            Console.WriteLine("end mainloop receiver");
        }


        private String[] splitter(String s)
        {
            return s.Split(':');   
        }

        private void nextLine()
        {
            try
            {   
               
                       line = connector.readBufferedLine();

                       while (line == null)
                       {
                           Thread.Sleep(TIME_OUT_WAITING_FOR_NEW_MESSAGE);
                          
                               
                               line = connector.readBufferedLine();
                          
                       }
            }
            catch (Exception e)
            {
                Console.WriteLine("following exception occured in Parser: ");
                Console.WriteLine(e.Message);
                throw e;
            }

            if (line != null)
            {
                s = splitter(line);
            }
            else
            {
                s = null;
            }
            
        }
        
        private bool sContains(String a, String b, String[] t)
        {
            if (t.Length < 2)
            {
                Console.WriteLine("Fehler: " + a + " und " + b + " not exist, array to short");
                return false;
            }
            if (t[0].Equals(a) && t[1].Equals(b)) { return true; } 
            else {
                Console.WriteLine("Fehler: " + a + " != "+t[0]+" und " + b+" != "+t[1]); 
                return false; 
            }
        }

        private void servermess()
        {
            nextLine(); //begin:yourid; end:1
            if (s!=null && s[0].Equals("begin")) // edit conny -> auch auf null überprüft, kam eine message vom server die nur begin drin hatte
            {
                switch (s[1])
                {
                    case "upd":
                        upd(); break;
                    case "map":
                        backend.setMap(map()); break;
                    case "del":
                        del(); break;
                    case "mes":
                        backend.addChatMessage(mes()); break;
                    case "result":
                        backend.setResult(cResult()); break;
                    case "challenge":
                        backend.setChallenge(challenge()); break;
                    case "time":
                        backend.setTime(time()); break;
                    case "online":
                        backend.setOnline(online()); break;
                    case "yourid":
                        backend.setYourID(yourid()); break;
                    case "player":
                        backend.setMyFigure(player()); break;
                    case "ents":
                        ents(); break; // ergänzt --> auf diesen befehl hin gibt der server  alle dragons und player zurück
                    case "players": // hat plötzlich fehler angezeigt, von wegen es kommt zweimal player vor, habe players draus gemacht.
                        players(); break;
                    default:
                        Console.WriteLine("!!!!!!-----------["+s[0]+":"+s[1]+"] is an invalid value at servermess");
                        break;
                } nextLine();
            }
            else start();

        }

        private void ents()
        { 
            if ( sContains("begin", "ents", s))
            {
                nextLine();
                while (!s[0].Equals("end"))
                {
                    if (s[0].Equals("begin") && s[1].Equals("player")) { backend.upd(player()); nextLine(); }

                    if (s[0].Equals("begin") && s[1].Equals("dragon")) { backend.upd(dragon()); nextLine(); }
                }

                if (!sContains("end", "ents", s))
                    Console.WriteLine("Fehler bei ents ");                   
            } 
        }


        private void players()
        { 
            if (sContains("begin", "players", s)  )
            {
                nextLine();
                while (!s[0].Equals("end"))
                {
                    if (s[0].Equals("begin") && s[1].Equals("player")) { backend.upd(player()); nextLine(); }

                   // if (s[0].Equals("begin") && s[1].Equals("dragon")) { backend.upd(dragon()); nextLine(); }
                }

                if (!sContains("end", "players", s) )
                    Console.WriteLine("Fehler bei player ");
            }
        }



        private Figure player()
        {
            int points = 20;
            int id = 0;
            String type = "";
            bool busy = true;
            String desc = "test";
            int x = 0;
            int y = 0;

            if (sContains("begin", "player", s))// id:228
            {
                nextLine();

                if (s[0].Equals("id")) { id = Convert.ToInt32(s[1]); nextLine(); }; //type:player
                if (s[0].Equals("type")) { type = s[1]; nextLine(); }; //busy:false
                if (s[0].Equals("busy")) { busy = Convert.ToBoolean(s[1]); nextLine(); }; //desc:player228
                if (s[0].Equals("desc")) { desc = s[1]; nextLine(); }; //x:5
                if (s[0].Equals("x")) { x = Convert.ToInt32(s[1]); nextLine(); };//y:9
                if (s[0].Equals("y")) { y = Convert.ToInt32(s[1]); nextLine(); }; //points:0
                if (s[0].Equals("points")) { points = Convert.ToInt32(s[1]); nextLine(); }; //end:player
                Console.WriteLine(id + " " + busy + " " + desc + " " + x + " " + y + " " + points + " " + type);

            }
            if (!(sContains("end", "player", s))) throw new Exception();
            return new Figure(points, id, FigureTypeEnum.PLAYER, busy, desc, x, y);
        }

        private int online()
        {
            int on = 0;
            if (sContains("begin", "online", s))
            {
                nextLine();
                on = Convert.ToInt32(s[0]);
                nextLine();
            }
            if (!(sContains("end", "online", s)))
                throw new Exception(); 
            return on;
        }

        private long time()
        {
            long t = 0;
            if (sContains("begin", "time", s))
            {
                nextLine();
                t = (long)Convert.ToDouble(s[0]); // geht das anders?
                nextLine();
            }
            if (!(sContains("end", "time", s))) throw new Exception(); 
            return t;
        }

        private Challenge challenge()
        {
            int id = 0;
            bool accepted = false;
            ChallengeTypeEnum type = ChallengeTypeEnum.DRAGON;
            if (sContains("begin", "challenge", s))
            {
                nextLine();
                if (s[0].Equals("id")) { id = Convert.ToInt32(s[1]); nextLine(); };
                if (s[0].Equals("type"))
                {
                    switch (s[1])
                    {
                        case "DRAGON":
                            type = ChallengeTypeEnum.DRAGON; break;
                        case "STAGHUNT":
                            type = ChallengeTypeEnum.STAGHUNT; break;
                        case "SKIRMISH":
                            type = ChallengeTypeEnum.SKIRMISH; break;
                        default:
                            throw new Exception();

                    } nextLine();
                }

                if (s[0].Equals("accepted")) { accepted = Convert.ToBoolean(s[1]); nextLine(); };
            }
            if (!(sContains("end", "challenge", s))) throw new Exception();
            return new Challenge(id, accepted, type);
        }

  

        private Result cResult()
        {
            int round = 0;
            bool running = false;
            int delay = 0;
            Opponent[] players = new Opponent[2];
            if (sContains("begin", "result", s))
            {
                nextLine();
                if (s[0].Equals("round")) { round = Convert.ToInt32(s[1]); nextLine(); }
                if (s[0].Equals("running")) { running = Convert.ToBoolean(s[1]); nextLine(); }
                if (s[0].Equals("delay")) { delay = Convert.ToInt32(s[1]); nextLine(); }
                // achtung, ändern zu opponent nötig
                if (s[0].Equals("begin") && s[1].Equals("opponents"))
                {
                    int i = 0;
                    nextLine();
                    while (!(s[0].Equals("end") && s[1].Equals("opponents")))
                    {
                        
                        players[i] = cOpponent();
                        i++;
                        nextLine();
                    }
                }

            }
            nextLine();
            if (!(sContains("end", "result", s))) throw new Exception();
            return new Result(round, running, delay, players);
        }

        private Opponent cOpponent()
        {
            int id = 0;
            int points = 0;
            int total = 0;
            Decision decision = null;
            if (sContains("begin", "opponent", s))
            {
                nextLine();
                if (s[0].Equals("id")) { id = Convert.ToInt32(s[1]); nextLine(); }
                if (s[0].Equals("decision")) { decision = cDecision(); nextLine(); }
                if (s[0].Equals("points")) { points = Convert.ToInt32(s[1]); nextLine(); }
                if (s[0].Equals("total")) { total = Convert.ToInt32(s[1]); nextLine(); }
            }
            if (!(sContains("end", "opponent", s))) throw new Exception();
            return new Opponent(id, points, total, decision);

        }

        private Decision cDecision()
        { 
            ActionEnum action = cAction();   
            return new Decision(action);
        }
        private ActionEnum cAction()
        { 
            switch (s[1])
            {
                case "FIGHT":
                    return ActionEnum.FIGHT;
                case "REST":
                    return ActionEnum.REST;
                case "STAG":
                    return ActionEnum.STAG;
                case "BUNNY":
                    return ActionEnum.BUNNY;
                case "SWORD":
                    return ActionEnum.SWORD;
                case "MAGIC":
                    return ActionEnum.MAGIC;
                case "ALCHEMY":
                    return ActionEnum.ALCHEMY;
                default:
                    throw new Exception();
            }
        }

        private Message mes()
        {
            int srcid = 0;
            String src = " ";
            String txt = " ";
            if (sContains("begin", "mes", s))
            {
                nextLine();
                if (s[0].Equals("srcid")) { srcid = Convert.ToInt32(s[1]); nextLine(); }
                if (s[0].Equals("src")) { src = s[1]; nextLine(); }
                if (s[0].Equals("txt")) { txt = s[1]; nextLine(); }
            }
            if (!(sContains("end", "mes", s))) throw new Exception();
            return new Message(srcid, src, txt);
        }

        private void del()
        {
            if (sContains("begin", "del", s))
            {
                nextLine();
                switch (s[0])
                {
                    case "dragon":
                        backend.del(dragon()); break;
                    case "player":
                        backend.del(player()); break;
                   // default: 
                       // throw new Exception();  ACHTUNG
                }
            }
            if (sContains("end", "del", s)) return;
        }

        private Map map()
        {
            Map map = null;
            int width = 0;
            int heigth = 0; 
            if (sContains("begin", "map", s))
            {
                nextLine();
                if (s[0].Equals("width"))
                {
                    width = Convert.ToInt32(s[1]);
                    nextLine();
                }
                if (s[0].Equals("height"))
                {
                    heigth = Convert.ToInt32(s[1]);
                    nextLine();
                }
                map = new Map(width, heigth);
                if (sContains("begin", "cells", s))
                {
                    nextLine();
                    while ((s.Length>1 && s[0].Equals("begin") && s[1].Equals("cell")) || !(sContains("end", "cells", s)))
                    {
                        map.setMapCell(mapcell());
                        nextLine();
                    }

                }
                if (sContains("end", "cells", s)) nextLine();
            }
            if (!(sContains("end", "map", s))) throw new Exception();
            return map;
        }

        private void upd()
        {
            if (sContains("begin", "upd", s))
            {
                nextLine(); //begin: player ; end:update
                if (s[0].Equals("begin"))
                {
                    switch (s[1])
                    {
                        case "dragon":
                            backend.upd(dragon()); break;
                        case "player":
                            backend.upd(player()); break;
                        case "cell":
                            mapcell(); break;
                        default:
                            Console.WriteLine("!!!!!!-----------[" + s[0] + ":" + s[1] + "] is an invalid value upd");
                            throw new Exception();
                    }
                } nextLine();
            }
            if (sContains("end", "upd", s)) return;
            else Console.WriteLine("---------- end:upd is missing");
        }

        private Figure dragon()
        {
            int id = 0;
            String type = " ";
            bool busy = true;
            String desc = " ";
            int x = 0;
            int y = 0;
            if (sContains("begin", "dragon", s))
            {
                nextLine();
                if (s[0].Equals("id")) { id = Convert.ToInt32(s[1]); nextLine(); };
                if (s[0].Equals("type")) { type = s[1]; nextLine(); };
                if (s[0].Equals("busy")) { busy = Convert.ToBoolean(s[1]); nextLine(); };
                if (s[0].Equals("desc")) { desc = s[1]; nextLine(); };
                if (s[0].Equals("x")) { x = Convert.ToInt32(s[1]); nextLine(); };
                if (s[0].Equals("y")) { y = Convert.ToInt32(s[1]); nextLine(); };
                Console.WriteLine(id + " " + busy + " " + desc + " " + x + " " + y);
            }
            if (!(sContains("end", "dragon", s))) throw new Exception(); // EXCEPTION-HANDLING!!!!!
            return new Figure(0, id, FigureTypeEnum.DRAGON, busy, desc, x, y);
        }


        private MapCell mapcell()
        {
            int row = 0;
            int col = 0;
            List<MapCellPropertyEnum> propList = null;
            if (sContains("begin", "cell", s))
            {
                nextLine(); // row 
                if (s[0].Equals("row")) { row = Convert.ToInt32(s[1]); nextLine(); };// col ; row
                if (s[0].Equals("col")) { col = Convert.ToInt32(s[1]); nextLine(); }; //begin props , col
                Console.WriteLine(" Mapcell created " + " row: " + row + " col: " + col); //------
                if (sContains("begin", "props", s)) { propList = props(); nextLine(); }
                if (propList == null)
                    throw new NullReferenceException("cellpropertylist must not be null");
            }
            if (!(sContains("end", "cell", s))) MAPCELL_ERROR++;//throw new Exception();
            return new MapCell(row, col, propList);
        }

        private List<MapCellPropertyEnum> props()
        {
            List<MapCellPropertyEnum> p = new List<MapCellPropertyEnum>();
                    
            if (sContains("begin", "props", s))
            {
                nextLine(); //props "wall"
                while (!s[0].Equals("end"))
                {
                    switch (s[0])
                    {
                        case "WATER":
                              p.Add(MapCellPropertyEnum.WATER); break;
                        case "FOREST": 
                              p.Add(MapCellPropertyEnum.FOREST); break;
                        case "WALL":
                            p.Add(MapCellPropertyEnum.WALL); break;
                        case "HUNTABLE":
                            p.Add(MapCellPropertyEnum.HUNTABLE); break;
                        case "WALKABLE":
                            p.Add(MapCellPropertyEnum.WALKABLE); break;
                        default: 
                            Console.WriteLine("unknown cell prop");break;                            
                    }
                    nextLine();
                }
            }// props end  

            if (!(sContains("end", "props", s))) MAPCELL_PROP_ERROR++;//throw new Exception();
            return p;

        }

        private int yourid()
        {
            int id = 0;
            if (sContains("begin", "yourid", s))
            {
                nextLine();// 228
                id = Convert.ToInt32(s[0]);
                nextLine();// end:yourid
                Console.WriteLine("your id= " + id);
            }
            //if (!(sContains("end", "yourid", s)));// kein fehler ausgeben 
            
            return id;
        }

        private void ans()
        {
            String ans;
            if (sContains("begin", "ans", s))
            {
                nextLine();
                ans = s[1] + " " + s[2];
            }
            if (sContains("end", "ans", s)) return;
        }
    
    }
}

